package com.tma.demoservice2;

import java.io.File;
import java.util.List;

import a.activity.ServiceUtil;
import a.io.FileHelper;
import a.location.LocationUtil;
import a.location.MyLocation;
import android.app.AlertDialog;
import android.app.PendingIntent;
import android.app.ProgressDialog;
import android.app.Service;
import android.content.ComponentName;
import android.content.DialogInterface;
import android.content.Intent;
import android.content.ServiceConnection;
import android.content.SharedPreferences;
import android.graphics.drawable.Drawable;
import android.location.Location;
import android.location.LocationManager;
import android.os.AsyncTask;
import android.os.AsyncTask.Status;
import android.os.Bundle;
import android.os.Environment;
import android.os.IBinder;
import android.os.RemoteException;
import android.preference.PreferenceManager;
import android.provider.Settings;
import android.util.Log;
import android.view.Menu;
import android.view.MenuItem;
import android.view.View;
import android.view.View.OnClickListener;
import android.widget.Button;
import android.widget.Toast;
import android.widget.ToggleButton;

import com.google.android.maps.GeoPoint;
import com.google.android.maps.MapActivity;
import com.google.android.maps.MapController;
import com.google.android.maps.MapView;
import com.google.android.maps.OverlayItem;
import com.tma.demoservice2.db.DatabaseHelper;
import com.tma.demoservice2.location.ILocationTracerService;
import com.tma.demoservice2.location.LocationChangedReceiver;
import com.tma.demoservice2.location.LocationTracerService;

public class MainActivity extends MapActivity implements OnClickListener {
    private static final String TAG = "MainActivity";

    private boolean GPS_PROVIDER = true;
    private boolean NETWORK_PROVIDER = false;
    private boolean PASSIVE_MODE = false;
    private float MIN_DISTANCE = 1.0f;
    private long MIN_TIME = 1000; // milisecond

    private LocationManager mLocationManager;
    private PendingIntent mPendingIntent;
    private ToggleButton mStatusButton;
    private ToggleButton mBindButton;
    private MapView mMapView;
    private Button mLoadLocationButton;

    private ILocationTracerService mService;
    private boolean mBound = false;
    private BackupDbTask mBackupDbTask = null;
    private RestoreDbTask mRestoreDbTask = null;
    private LoadLocationTask mLoadLocationTask = null;

    private ServiceConnection mServiceConnection = new ServiceConnection() {
        public void onServiceDisconnected(ComponentName name) {
            Log.i(TAG, "onServiceDisconnected");
            mService = null;
        }

        public void onServiceConnected(ComponentName name, IBinder service) {
            Log.i(TAG, "onServiceConnected");
            mService = ILocationTracerService.Stub.asInterface(service);
            mStatusButton.setChecked(true);
            mLoadLocationButton.setEnabled(true);
        }
    };

    @Override
    public void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);

        mStatusButton = (ToggleButton) findViewById(R.id.btn_service_status);
        mBindButton = (ToggleButton)findViewById(R.id.btn_bind_service);
        
        mMapView = (MapView) findViewById(R.id.mv_map);
        mLoadLocationButton = (Button) findViewById(R.id.btn_load_location);

        mBindButton.setOnClickListener(this);
        mLoadLocationButton.setOnClickListener(this);

        Intent intent = new Intent(this, LocationChangedReceiver.class);
        mPendingIntent = PendingIntent.getBroadcast(this, 0, intent, PendingIntent.FLAG_UPDATE_CURRENT);

        mLocationManager = (LocationManager) getSystemService(Service.LOCATION_SERVICE);
    }

    @Override
    protected void onResume() {
        super.onResume();
        checkProvidersEnable();

        mBindButton.setChecked(mBound);
        mStatusButton.setChecked(ServiceUtil.isServiceRunning(this, LocationTracerService.class));
        mLoadLocationButton.setEnabled(mBound);
    }

    @Override
    protected void onDestroy() {
        //unregisterLocationUpdate();
        super.onDestroy();
        unbindService(false);
    }

    @Override
    public boolean onCreateOptionsMenu(Menu menu) {
        getMenuInflater().inflate(R.menu.activity_main, menu);
        return true;
    }

    @Override
    public boolean onPrepareOptionsMenu(Menu menu) {
        MenuItem item = menu.findItem(R.id.menu_stop_service);

        boolean serviceRunning = ServiceUtil.isServiceRunning(this, LocationTracerService.class);
        item.setEnabled(serviceRunning);

        if (mBackupDbTask != null && mBackupDbTask.getStatus() != Status.FINISHED) {
            MenuItem backupItem = menu.findItem(R.id.menu_backup_db);
            backupItem.setEnabled(false);
        }
        if (mRestoreDbTask != null && mRestoreDbTask.getStatus() != Status.FINISHED) {
            MenuItem restoreItem = menu.findItem(R.id.menu_restore_db);
            restoreItem.setEnabled(false);
        }
        return true;
    }

    @Override
    public boolean onMenuItemSelected(int featureId, MenuItem item) {
        switch (item.getItemId()) {
        case R.id.menu_start_service:
            startService(new Intent(this, LocationTracerService.class));
            mStatusButton.setChecked(true);
            break;
        case R.id.menu_stop_service:
            unbindService();
            stopService(new Intent(this, LocationTracerService.class));
            mStatusButton.setChecked(false);
            mLoadLocationButton.setEnabled(false);
            mBindButton.setChecked(false);
            break;
        case R.id.menu_backup_db:
            mBackupDbTask = new BackupDbTask();
            mBackupDbTask.execute();
            break;
        case R.id.menu_restore_db:
            mRestoreDbTask = new RestoreDbTask();
            mRestoreDbTask.execute();
            break;
        case R.id.menu_settings:
            Intent i = new Intent(this, PrefsActivity.class);
            startActivityForResult(i, 0);
            break;
        default:
            break;
        }
        return super.onMenuItemSelected(featureId, item);
    }

    @Override
    protected boolean isRouteDisplayed() {
        return false;
    }

    public void onClick(View v) {
        switch (v.getId()) {
        case R.id.btn_bind_service:
            if (mBindButton.isChecked()) {
                bindService();
            } else {
                unbindService();
                mLoadLocationButton.setEnabled(false);
            }
            break;
        case R.id.btn_load_location:
            mLoadLocationTask = new LoadLocationTask();
            mLoadLocationTask.execute();
            break;
        default:
            break;
        }
    }

    @Override
    protected void onActivityResult(int requestCode, int resultCode, Intent data) {
        super.onActivityResult(requestCode, resultCode, data);
        loadPrefs();
    }

    private void bindService() {
        //Intent intent = new Intent(this, LocationTracerService.class);
        Intent intent = new Intent(ILocationTracerService.class.getName());
        mBound = bindService(intent, mServiceConnection, Service.BIND_AUTO_CREATE);
        registerLocationUpdate();
    }

    private void unbindService() {
        unbindService(true);
    }
    
    private void unbindService(boolean unregisterLocationUpdate) {
        if (mBound) {
            unbindService(mServiceConnection);
            mBound = false;
        }
        if (unregisterLocationUpdate) {
            unregisterLocationUpdate();
        }
    }

    private void checkProvidersEnable() {
        boolean gps = mLocationManager
                .isProviderEnabled(LocationManager.GPS_PROVIDER);
        boolean network = mLocationManager
                .isProviderEnabled(LocationManager.NETWORK_PROVIDER);

        if (gps == false && network == false) {
            DialogInterface.OnClickListener dialogClickListener = new DialogInterface.OnClickListener() {
                public void onClick(DialogInterface dialog, int which) {
                    switch (which) {
                    case DialogInterface.BUTTON_POSITIVE:
                        // open location setting
                        Intent intent = new Intent(Settings.ACTION_LOCATION_SOURCE_SETTINGS);
                        startActivity(intent);
                        break;

                    case DialogInterface.BUTTON_NEGATIVE:
                        finish(); 
                        break;
                    }
                }
            };

            AlertDialog.Builder builder = new AlertDialog.Builder(this);
            builder.setMessage("Are you want to open location setting?")
                    .setPositiveButton("Yes", dialogClickListener)
                    .setNegativeButton("No", dialogClickListener).show();
        }
    }

    private void registerLocationUpdate() {
        Log.i(TAG, "registerLocationUpdate");
        if (GPS_PROVIDER) {
            mLocationManager.requestLocationUpdates(LocationManager.GPS_PROVIDER, MIN_TIME, MIN_DISTANCE, mPendingIntent);
        }
        if (NETWORK_PROVIDER) {
            mLocationManager.requestLocationUpdates(LocationManager.NETWORK_PROVIDER, MIN_TIME, MIN_DISTANCE, mPendingIntent);
        }
        if (PASSIVE_MODE) {
            mLocationManager.requestLocationUpdates(LocationManager.PASSIVE_PROVIDER, MIN_TIME, MIN_DISTANCE, mPendingIntent);
        }
    }

    private void unregisterLocationUpdate() {
        Log.i(TAG, "unregisterLocationUpdate");
        mLocationManager.removeUpdates(mPendingIntent);
    }

    private void backupDb() {
        try {
            File sd = Environment.getExternalStorageDirectory();
            if (sd.canWrite()) {
                File source = getDatabasePath(DatabaseHelper.DATABASE_NAME);
                File dest = new File(sd, DatabaseHelper.DATABASE_NAME);

                FileHelper.copyFile(source, dest);
            }
        } catch (Exception e) {
            Log.e(TAG, e.getMessage(), e);
        }
    }

    private void restoreDb() {
        try {
            File sd = Environment.getExternalStorageDirectory();
            File source = new File(sd, DatabaseHelper.DATABASE_NAME);
            if (!source.exists()) {
                Toast.makeText(this, "Can not find " + source, Toast.LENGTH_SHORT).show();
                return;
            }

            File dest = getDatabasePath(DatabaseHelper.DATABASE_NAME);
            FileHelper.copyFile(source, dest);
        } catch (Exception e) {
            Log.e(TAG, e.getMessage(), e);
        }
    }

    private void loadPrefs() {
        SharedPreferences prefs = PreferenceManager.getDefaultSharedPreferences(this);

        GPS_PROVIDER = prefs.getBoolean(getString(R.string.pref_gps_key), true);
        NETWORK_PROVIDER = prefs.getBoolean(getString(R.string.pref_network_key), false);
        PASSIVE_MODE = prefs.getBoolean(getString(R.string.pref_passive_key), false);

        Log.i(TAG, "gps: " + GPS_PROVIDER + "; network: " + NETWORK_PROVIDER + "; passive: " + PASSIVE_MODE);

        String temp = prefs.getString(getString(R.string.pref_periodic_time_key), getString(R.string.pref_periodic_option_default_value));
        long seconds = Long.parseLong(temp);
        MIN_TIME = 1000 * seconds;
        Log.i(TAG, "min time: " + seconds + " (second)");

        temp = prefs.getString(getString(R.string.pref_min_distance_key), getString(R.string.pref_min_distance_option_default_value));
        MIN_DISTANCE = Float.parseFloat(temp);
        Log.i(TAG, "min distance: " + MIN_DISTANCE + " (meter)");

        unregisterLocationUpdate();
        registerLocationUpdate();
    }

    private class BackupDbTask extends AsyncTask<Void, Void, Void> {
        private ProgressDialog mProgressDialog;
        @Override
        protected void onPreExecute() {
            mProgressDialog = ProgressDialog.show(MainActivity.this, "", "Please wait...");
        }

        @Override
        protected Void doInBackground(Void... params) {
            backupDb();
            return null;
        }

        @Override
        protected void onPostExecute(Void result) {
            if (mProgressDialog.isShowing()) {
                mProgressDialog.dismiss();
            }
        }
        
    }

    private class RestoreDbTask extends AsyncTask<Void, Void, Void> {
        private ProgressDialog mProgressDialog;
        @Override
        protected void onPreExecute() {
            mProgressDialog = ProgressDialog.show(MainActivity.this, "", "Please wait...");
        }

        @Override
        protected Void doInBackground(Void... params) {
            restoreDb();
            return null;
        }

        @Override
        protected void onPostExecute(Void result) {
            if (mProgressDialog.isShowing()) {
                mProgressDialog.dismiss();
            }
        }
        
    }

    private class LoadLocationTask extends
            AsyncTask<Void, Void, List<Location>> {
        private ProgressDialog mProgressDialog;

        @Override
        protected void onPreExecute() {
            mProgressDialog = ProgressDialog.show(MainActivity.this,
                    "Invoke Service", "Loading location...");
        }

        @Override
        protected List<Location> doInBackground(Void... params) {
            try {
                // get all locations
                return mService.getLocations(-1);
            } catch (RemoteException e) {
                Log.e(TAG, e.getMessage(), e);
            }
            return null;
        }

        @Override
        protected void onPostExecute(List<Location> result) {
            mProgressDialog.cancel();

            if (result == null) {
                return;
            }

            mMapView.getOverlays().clear();

            Drawable marker = getResources().getDrawable(R.drawable.point_marker);
            marker.setBounds(
                    (int) (-marker.getIntrinsicWidth() / 2), 
                    -marker.getIntrinsicHeight(), 
                    (int) (marker.getIntrinsicWidth() / 2), 
                    0);

            MyLocation tracers = new MyLocation(marker);
            mMapView.getOverlays().add(tracers);

            for (Location location : result) {
                GeoPoint geoPoint = new GeoPoint((int)(location.getLatitude() * 1000000), (int)(location.getLongitude() * 1000000));
                Log.i(TAG, "processing GeoPoint: " + geoPoint);
                OverlayItem item = new OverlayItem(geoPoint, "", "");
                tracers.add(item, false);
            }
            tracers.refresh();

            if (tracers.getItems().size() > 0) {
                GeoPoint center = LocationUtil.getCenterPoint(tracers.getItems(), 0);
                int latSpan = tracers.getLatSpanE6();
                int lonSpan = tracers.getLonSpanE6();
                Log.v("Overlays", "Lat span is " + latSpan);
                Log.v("Overlays", "Lng span is " + lonSpan);
                MapController mapController = mMapView.getController();
                mapController.animateTo(center);
                mapController.zoomToSpan((int)(latSpan*1.5), (int)(lonSpan*1.5));
            }
        }
    }
}
